/////////////////////////////////////////////////////////////////
/*
  Button2.cpp - Arduino Library to simplify working with buttons.
  Created by Lennart Hennigs, October 28, 2017.
*/
/////////////////////////////////////////////////////////////////

#include "Arduino.h"
#include "Button2.h"

/////////////////////////////////////////////////////////////////

Button2::Button2(byte attachTo, byte buttonMode /*= INPUT_PULLUP*/, unsigned int debounceTimeout /*= DEBOUNCE_MS*/)
{
    pin = attachTo;
    setDebounceTime(debounceTimeout);
    pinMode(attachTo, buttonMode);
}

/////////////////////////////////////////////////////////////////

bool Button2::operator==(Button2 &rhs)
{
    return (this == &rhs);
}

/////////////////////////////////////////////////////////////////

void Button2::setDebounceTime(unsigned int ms)
{
    debounce_time_ms = ms;
}

/////////////////////////////////////////////////////////////////

void Button2::setChangedHandler(CallbackFunction f)
{
    change_cb = f;
}

/////////////////////////////////////////////////////////////////

void Button2::setPressedHandler(CallbackFunction f)
{
    pressed_cb = f;
}

/////////////////////////////////////////////////////////////////

void Button2::setReleasedHandler(CallbackFunction f)
{
    released_cb = f;
}

/////////////////////////////////////////////////////////////////

void Button2::setClickHandler(CallbackFunction f)
{
    click_cb = f;
}

/////////////////////////////////////////////////////////////////

void Button2::setTapHandler(CallbackFunction f)
{
    tap_cb = f;
}

/////////////////////////////////////////////////////////////////

void Button2::setLongClickHandler(CallbackFunction f)
{
    long_cb = f;
}

/////////////////////////////////////////////////////////////////

void Button2::setDoubleClickHandler(CallbackFunction f)
{
    double_cb = f;
}

/////////////////////////////////////////////////////////////////

void Button2::setTripleClickHandler(CallbackFunction f)
{
    triple_cb = f;
}

/////////////////////////////////////////////////////////////////

unsigned int Button2::wasPressedFor()
{
    return down_time_ms;
}

/////////////////////////////////////////////////////////////////

boolean Button2::isPressed()
{
    return (state == LOW);
}

/////////////////////////////////////////////////////////////////

unsigned int Button2::getNumberOfClicks()
{
    return click_count;
}

/////////////////////////////////////////////////////////////////

unsigned int Button2::getClickType()
{
    return last_click_type;
}

/////////////////////////////////////////////////////////////////

void Button2::loop()
{
    if (pin == 0xFF)return;

    prev_state = state;
    state = digitalRead(pin);

    // is button pressed?
    if (prev_state == HIGH && state == LOW) {
        down_ms = millis();
        pressed_triggered = false;
        click_count++;
        click_ms = down_ms;

        // is the button released?
    } else if (prev_state == LOW && state == HIGH) {
        down_time_ms = millis() - down_ms;
        // is it beyond debounce time?
        if (down_time_ms >= debounce_time_ms) {
            // trigger release
            if (change_cb != NULL) change_cb (*this);
            if (released_cb != NULL) released_cb (*this);
            // trigger tap
            if (tap_cb != NULL) tap_cb (*this);
            // was it a longclick? (preceeds single / double / triple clicks)
            if (down_time_ms >= LONGCLICK_MS) {
                longclick_detected = true;
            }
        }

        // trigger pressed event (after debounce has passed)
    } else if (state == LOW && !pressed_triggered && (millis() - down_ms >= debounce_time_ms)) {
        if (change_cb != NULL) change_cb (*this);
        if (pressed_cb != NULL) pressed_cb (*this);
        pressed_triggered = true;

        // is the button pressed and the time has passed for multiple clicks?
    } else if (state == HIGH && millis() - click_ms > DOUBLECLICK_MS) {
        // was there a longclick?
        if (longclick_detected) {
            // was it part of a combination?
            if (click_count == 1) {
                last_click_type = LONG_CLICK;
                if (long_cb != NULL) long_cb (*this);
            }
            longclick_detected = false;
            // determine the number of single clicks
        } else if (click_count > 0) {
            switch (click_count) {
            case 1:
                last_click_type = SINGLE_CLICK;
                if (click_cb != NULL) {
                    click_cb (*this);
                }
                break;
            case 2:
                last_click_type = DOUBLE_CLICK;
                if (double_cb != NULL) double_cb (*this);
                break;
            case 3:
                last_click_type = TRIPLE_CLICK;
                if (triple_cb != NULL) triple_cb (*this);
                break;
            }
        }
        click_count = 0;
        click_ms = 0;
    }
}

/////////////////////////////////////////////////////////////////